/*
 * Copyright (C) 2005 Johan Maasing johan at zoom.nu Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package nu.zoom.swing.desktop.plugin.portunis.impl;

import java.awt.Color;
import java.awt.Font;
import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.security.auth.x500.X500Principal;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextArea;

import nu.zoom.swing.desktop.Workbench;
import nu.zoom.swing.desktop.plugin.portunis.Utils;
import nu.zoom.swing.layout.VerticalPanel;

import org.ops4j.gaderian.Messages;

/**
 * A factory for components that displays certificate information.
 * 
 * @author $Author: johan $
 * @version $Revision: 1.2 $
 */
public class CertificateComponentFactory
{

	private final KeyComponentFactory keyComponentFactory;
	private final Workbench workbench;
	private final Messages messages;

	public CertificateComponentFactory(final Workbench workbench, final Messages messages) {
		this.messages = messages;
		this.workbench = workbench;
		keyComponentFactory = new KeyComponentFactory(messages);
	}

	JComponent getCertificateComponent(final Certificate cert)
	{
		VerticalPanel detailsComponent = new VerticalPanel();

		detailsComponent.addRow(messages
				.getMessage("keystore.display.cert.type"), cert.getType());
		if (cert instanceof X509Certificate) {
			X509Certificate x509Cert = (X509Certificate) cert;

			// Subject
			X500Principal subject = x509Cert.getSubjectX500Principal();
			final JLabel nameValue = new JLabel(subject.getName(X500Principal.CANONICAL));
			final String tooltip = subject.getName(X500Principal.RFC2253);
			if (tooltip != null) {
				nameValue.setToolTipText(tooltip);
			}
			final JLabel nameLabel = new JLabel(messages
					.getMessage("keystore.display.cert.subject"));
			detailsComponent.addRow(nameLabel, nameValue);
			// Subject alternate names
			try {
				buildAlternateNames(detailsComponent, x509Cert
						.getSubjectAlternativeNames());
			} catch (CertificateParsingException exc) {
				workbench
						.getErrorReporter()
						.reportError(
								messages
										.getMessage("keystore.display.cert.alternatename.parseerror"),
								exc);
			}

			// Date
			final DateFormat dateFormatter = DateFormat.getDateTimeInstance(
					DateFormat.SHORT, DateFormat.SHORT);
			final Date notBefore = x509Cert.getNotBefore();
			final String notBeforeString = (notBefore != null) ? dateFormatter
					.format(notBefore) : messages
					.getMessage("keystore.display.cert.value.null");
			detailsComponent.addRow(messages
					.getMessage("keystore.display.cert.notbefore"),
					notBeforeString);

			final Date notAfter = x509Cert.getNotAfter();
			final String notAfterString = (notAfter != null) ? dateFormatter
					.format(notAfter) : messages
					.getMessage("keystore.display.cert.value.null");
			detailsComponent.addRow(messages
					.getMessage("keystore.display.cert.notafter"),
					notAfterString);
			// Version
			final int version = x509Cert.getVersion();
			detailsComponent.addRow(messages
					.getMessage("keystore.display.cert.version"), "" + version);
			// Serial
			final BigInteger serial = x509Cert.getSerialNumber();
			if (serial != null) {
				detailsComponent.addRow(messages
						.getMessage("keystore.display.cert.serial"), serial
						.toString());
			} else {
				detailsComponent.addRow(messages
						.getMessage("keystore.display.cert.serial"), messages
						.getMessage("keystore.display.cert.value.null"));
			}

			// Issuer
			final X500Principal issuer = x509Cert.getIssuerX500Principal();
			detailsComponent.addRow(messages
					.getMessage("keystore.display.cert.issuer"), issuer.getName());
			// Issuer alternate names
			try {
				buildAlternateNames(detailsComponent, x509Cert
						.getIssuerAlternativeNames());
			} catch (CertificateParsingException exc) {
				workbench
						.getErrorReporter()
						.reportError(
								messages
										.getMessage("keystore.display.cert.alternatename.parseerror"),
								exc);
			}
			// Key usage
			buildKeyUsage(detailsComponent, x509Cert);

			// SigAlgName
			final String sigAlgName = (x509Cert.getSigAlgName() != null) ? x509Cert
					.getSigAlgName() : messages
					.getMessage("keystore.display.cert.value.null");
			detailsComponent
					.addRow(messages
							.getMessage("keystore.display.cert.sigalgname"),
							sigAlgName);
			// SigAlgOID
			final String sigAlgOid = (x509Cert.getSigAlgOID() != null) ? x509Cert
					.getSigAlgOID() : messages
					.getMessage("keystore.display.cert.value.null");
			detailsComponent.addRow(messages
					.getMessage("keystore.display.cert.sigalgoid"), sigAlgOid);
			// Signature
			final String signature = Utils.getHexString(x509Cert.getSignature(), 80);

			final Font monospacedFont = Font.decode("Monospaced");
			final JTextArea signatureArea = new JTextArea(signature);
			signatureArea.setFont(monospacedFont);
			signatureArea.setEditable(false);

			detailsComponent.addRow(new JLabel(messages
					.getMessage("keystore.display.cert.signature")),
					signatureArea);
		}
		// Public key
		final String publicKeyLabel = messages.getMessage("keystore.key.public");
		if (cert.getPublicKey() != null) {
			final JComponent publicKeyComponent = keyComponentFactory
					.getKeyDescription(cert.getPublicKey());
			publicKeyComponent.setBorder(BorderFactory
					.createTitledBorder(publicKeyLabel));
			detailsComponent.addRow(publicKeyComponent);
		} else {
			detailsComponent.addRow(publicKeyLabel, messages
					.getMessage("keystore.display.cert.value.null"));
		}
		return detailsComponent;
	}

	private void buildKeyUsage(VerticalPanel detailsComponent,
			X509Certificate cert)
	{
		final boolean[] keyUsage = cert.getKeyUsage();
		final JLabel keyUsageLabel = new JLabel(messages
				.getMessage("keystore.display.cert.keyusage"));
		final JComponent keyUsageComponent;
		if (keyUsage == null) {
			keyUsageComponent = new JLabel(messages
					.getMessage("keystore.display.cert.keyusage.notknown"));
		} else {
			if (keyUsage.length < 9) {
				keyUsageComponent = new JLabel(messages
						.getMessage("keystore.display.cert.keyusage.toshort"));
			} else {
				final JCheckBox[] keyUsageComponents = new JCheckBox[9];

				keyUsageComponents[0] = new JCheckBox("Digital signature",
						keyUsage[0]);
				keyUsageComponents[1] = new JCheckBox("Non-Repudiation",
						keyUsage[1]);
				keyUsageComponents[2] = new JCheckBox("Key Encipherment",
						keyUsage[2]);
				keyUsageComponents[3] = new JCheckBox("Data Encipherment",
						keyUsage[3]);
				keyUsageComponents[4] = new JCheckBox("Key Agreement",
						keyUsage[4]);
				keyUsageComponents[5] = new JCheckBox(
						"Key Certificate Signing", keyUsage[5]);
				keyUsageComponents[6] = new JCheckBox("CRL Signing",
						keyUsage[6]);
				keyUsageComponents[7] = new JCheckBox("Encipher Only",
						keyUsage[7]);
				keyUsageComponents[8] = new JCheckBox("Decipher Only",
						keyUsage[8]);

				keyUsageComponent = new VerticalPanel();
				keyUsageComponent.setBorder(BorderFactory
						.createLineBorder(Color.DARK_GRAY));
				for (int i = 0; i < keyUsageComponents.length; i++) {
					keyUsageComponents[i].setEnabled(false);
					((VerticalPanel) keyUsageComponent)
							.addRow(keyUsageComponents[i]);
				}
			}
		}
		detailsComponent.addRow(keyUsageLabel, keyUsageComponent);
	}

	private void buildAlternateNames(final VerticalPanel detailsComponent,
			final Collection<List<?>> alternateNamesCollection)
	{
		if (alternateNamesCollection != null) {
			final List[] alternateNames = alternateNamesCollection
					.toArray(new List[alternateNamesCollection.size()]);
			for (int i = 0; i < alternateNames.length; i++) {
				final Object anamevalue = alternateNames[i].get(1);
				final String value = (anamevalue instanceof byte[]) ? Utils
						.getHexString((byte[]) anamevalue) : anamevalue
						.toString();
				final String label;
				if (alternateNames[i].size() > 1) {
					Integer type = (Integer) alternateNames[i].get(0);
					switch (type.intValue()) {
					case 0:
						label = "otherName";
						break;
					case 1:
						label = "rfc822Name";
						break;
					case 2:
						label = "DNSName";
						break;
					case 3:
						label = "x400Address";
						break;
					case 4:
						label = "directoryName";
						break;
					case 5:
						label = "ediPartyName";
						break;
					case 6:
						label = "URI";
						break;
					case 7:
						label = "IPAddress";
						break;
					case 8:
						label = "registeredID";
						break;
					default:
						label = messages
								.getMessage("keystore.display.cert.alternatename.unknowntype");
						break;
					}
					detailsComponent.addRow(label, value);
				} else {
					workbench
							.getErrorReporter()
							.reportError(
									messages
											.getMessage("keystore.display.cert.alternatename.parseerror"));
				}
			}
		}
	}
}
